package main

import (
	"fmt"
	"image"
	"image/color"
	"io/fs"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"
	"unsafe"

	"gioui.org/app"
	"gioui.org/io/key"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/op/clip"
	"gioui.org/op/paint"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
)

// Windows API 相关定义
var (
	shell32                  = syscall.NewLazyDLL("shell32.dll")
	procSHBrowseForFolderW   = shell32.NewProc("SHBrowseForFolderW")
	procSHGetPathFromIDListW = shell32.NewProc("SHGetPathFromIDListW")
	ole32                    = syscall.NewLazyDLL("ole32.dll")
	procCoTaskMemFree        = ole32.NewProc("CoTaskMemFree")
)

type BROWSEINFO struct {
	HwndOwner      uintptr
	PidlRoot       uintptr
	PszDisplayName *uint16
	LpszTitle      *uint16
	UlFlags        uint32
	Lpfn           uintptr
	LParam         uintptr
	IImage         int32
}

// UI相关变量
var (
	bgColor      = color.NRGBA{R: 240, G: 240, B: 240, A: 255}
	textColor    = color.NRGBA{R: 0, G: 0, B: 0, A: 255}
	buttonColor  = color.NRGBA{R: 0, G: 123, B: 255, A: 255}
	successColor = color.NRGBA{R: 40, G: 167, B: 69, A: 255}
	linkColor    = color.NRGBA{R: 0, G: 123, B: 255, A: 255}
)

// 搜索结果项
type SearchResult struct {
	Path           string
	CopyButton     *widget.Clickable
	FolderButton   *widget.Clickable
	OpenFileButton *widget.Clickable
	OpenWithButton *widget.Clickable
	NotepadButton  *widget.Clickable
	IsDirectory    bool // 标记是否为目录
}

// 应用状态
type AppState struct {
	dirEntry     widget.Editor
	targetEntry  widget.Editor
	results      []*SearchResult
	searchButton *widget.Clickable
	stopButton   *widget.Clickable
	browseButton *widget.Clickable
	isSearching  bool
	shouldStop   bool
	errorMsg     string
	successMsg   string // 添加成功消息字段
	th           *material.Theme
	shouldExit   bool
	// 新增：用于保存滚动列表状态，支持鼠标滚轮和滚动条
	resultList widget.List
}

// 初始化应用状态
func newAppState() *AppState {
	app := &AppState{
		searchButton: new(widget.Clickable),
		stopButton:   new(widget.Clickable),
		browseButton: new(widget.Clickable),
		th:           material.NewTheme(),
		resultList:   widget.List{List: layout.List{Axis: layout.Vertical}},
	}

	// 设置输入框为单行模式
	app.dirEntry.SingleLine = true
	app.targetEntry.SingleLine = true

	return app
}

// 打开文件夹选择对话框 (Windows)
func selectFolder() (string, error) {
	displayName := make([]uint16, 260)
	title, _ := syscall.UTF16PtrFromString("选择搜索目录")

	bi := BROWSEINFO{
		PszDisplayName: &displayName[0],
		LpszTitle:      title,
		UlFlags:        0x00000040, // BIF_NEWDIALOGSTYLE
	}

	ret, _, _ := procSHBrowseForFolderW.Call(uintptr(unsafe.Pointer(&bi)))
	if ret == 0 {
		return "", fmt.Errorf("用户取消了选择")
	}

	path := make([]uint16, 260)
	ret2, _, _ := procSHGetPathFromIDListW.Call(ret, uintptr(unsafe.Pointer(&path[0])))
	if ret2 == 0 {
		procCoTaskMemFree.Call(ret)
		return "", fmt.Errorf("获取路径失败")
	}

	procCoTaskMemFree.Call(ret)
	return syscall.UTF16ToString(path), nil
}

// 直接打开文件（使用系统默认程序）
func openFileDirectly(filePath string) error {
	cmd := exec.Command("cmd", "/c", "start", "", filePath)
	return cmd.Start()
}

// 用记事本打开文件
func openFileWithNotepad(filePath string) error {
	cmd := exec.Command("notepad.exe", filePath)
	return cmd.Start()
}

// 显示"打开方式"对话框
func openFileWithDialog(filePath string) error {
	// 使用 rundll32 调用 Windows 的"打开方式"对话框
	cmd := exec.Command("rundll32.exe", "shell32.dll,OpenAs_RunDLL", filePath)
	return cmd.Start()
}

// 复制路径到剪贴板 (Windows)
func copyToClipboard(text string) error {
	cmd := exec.Command("cmd", "/c", "echo "+text+"|clip")
	return cmd.Run()
}

// 在资源管理器中打开文件所在的文件夹
func openFolderInExplorer(filePath string) error {
	dir := filepath.Dir(filePath)
	cmd := exec.Command("explorer.exe", dir)
	return cmd.Start()
}

// 安全的文件搜索函数，支持中断搜索
func searchFilesSafe(dir, target string, results *[]*SearchResult, shouldStop *bool) error {
	if target == "" {
		return fmt.Errorf("请输入要搜索的文件名")
	}

	target = strings.ToLower(target)

	return filepath.WalkDir(dir, func(filePath string, d fs.DirEntry, err error) error {
		// 检查是否需要停止搜索
		if *shouldStop {
			return fmt.Errorf("搜索已停止")
		}

		// 跳过权限不足的目录，继续搜索其他目录
		if err != nil {
			// 检查是否是权限错误
			if os.IsPermission(err) {
				fmt.Printf("跳过无权限访问的目录: %s\n", filePath)
				return filepath.SkipDir // 跳过这个目录，继续其他目录
			}
			return nil // 忽略其他错误，继续搜索
		}

		// 跳过系统目录和隐藏目录
		if d.IsDir() {
			name := filepath.Base(filePath)
			if strings.HasPrefix(name, "$") || strings.HasPrefix(name, ".") {
				return filepath.SkipDir
			}
		}

		// 搜索文件和文件夹
		fileName := strings.ToLower(filepath.Base(filePath))
		if strings.Contains(fileName, target) {
			result := &SearchResult{
				Path:           filePath,
				CopyButton:     new(widget.Clickable),
				FolderButton:   new(widget.Clickable),
				OpenFileButton: new(widget.Clickable),
				OpenWithButton: new(widget.Clickable),
				NotepadButton:  new(widget.Clickable),
				IsDirectory:    d.IsDir(),
			}
			*results = append(*results, result)
		}

		return nil
	})
}

// 处理键盘事件
func (window *AppState) handleKeyEvents(gtx layout.Context) {
	// 处理全局键盘事件
	for {
		event, ok := gtx.Event(key.Filter{Name: key.NameReturn})
		if !ok {
			break
		}
		if e, ok := event.(key.Event); ok {
			if e.State == key.Press {
				// 当按下回车键时，如果不在搜索中就开始搜索
				if !window.isSearching {
					window.performSearch()
				}
			}
		}
	}
}

// 执行搜索
func (app *AppState) performSearch() {
	if app.isSearching {
		return // 防止重复搜索
	}

	app.isSearching = true
	app.shouldStop = false
	app.errorMsg = ""
	app.successMsg = "" // 清除成功消息
	app.results = nil

	// 在goroutine中执行搜索，避免阻塞UI
	go func() {
		defer func() {
			app.isSearching = false
		}()

		// 去除输入内容的首尾空格
		dir := strings.TrimSpace(app.dirEntry.Text())
		target := strings.TrimSpace(app.targetEntry.Text())

		if dir == "" {
			app.errorMsg = "请选择或输入搜索目录"
			return
		}

		if target == "" {
			app.errorMsg = "请输入要搜索的文件名"
			return
		}

		// 检查目录是否存在
		if _, err := os.Stat(dir); os.IsNotExist(err) {
			app.errorMsg = "指定的目录不存在"
			return
		}

		fmt.Printf("开始搜索: 目录=%s, 文件名=%s\n", dir, target)
		err := searchFilesSafe(dir, target, &app.results, &app.shouldStop)
		if err != nil {
			if app.shouldStop {
				app.errorMsg = "搜索已被用户停止"
				fmt.Println("搜索已停止")
			} else {
				app.errorMsg = err.Error()
			}
			return
		}

		if len(app.results) == 0 && !app.shouldStop {
			app.errorMsg = "未找到匹配的文件"
		} else if !app.shouldStop {
			fmt.Printf("搜索完成，找到 %d 个文件\n", len(app.results))
		}
	}()
}

// 停止搜索
func (app *AppState) stopSearch() {
	if app.isSearching {
		app.shouldStop = true
		fmt.Println("用户请求停止搜索")
	}
}

// 渲染标题栏
func (app *AppState) renderTitle(gtx layout.Context) layout.Dimensions {
	return layout.Inset{Top: 16, Left: 16, Right: 16}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		title := material.H5(app.th, "本地文件搜索器")
		title.Color = textColor
		return title.Layout(gtx)
	})
}

// 渲染路径输入区域
func (app *AppState) renderPathInput(gtx layout.Context) layout.Dimensions {
	return layout.Inset{Top: 16, Left: 16, Right: 16}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
			layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
				editor := material.Editor(app.th, &app.dirEntry, "输入目录路径或点击浏览按钮选择")
				editor.Color = textColor
				return editor.Layout(gtx)
			}),
			layout.Rigid(func(gtx layout.Context) layout.Dimensions {
				return layout.Inset{Left: unit.Dp(8)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
					if app.browseButton.Clicked(gtx) {
						go func() {
							if path, err := selectFolder(); err == nil {
								app.dirEntry.SetText(path)
							}
						}()
					}
					btn := material.Button(app.th, app.browseButton, "浏览")
					btn.Background = successColor
					return btn.Layout(gtx)
				})
			}),
		)
	})
}

// 渲染文件名输入
func (app *AppState) renderFileNameInput(gtx layout.Context) layout.Dimensions {
	return layout.Inset{Top: 16, Left: 16, Right: 16}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		editor := material.Editor(app.th, &app.targetEntry, "输入要搜索的文件名（支持部分匹配）")
		editor.Color = textColor
		return editor.Layout(gtx)
	})
}

// 渲染搜索按钮
func (app *AppState) renderSearchButton(gtx layout.Context) layout.Dimensions {
	return layout.Inset{Top: 16, Left: 16, Right: 16}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
			layout.Rigid(func(gtx layout.Context) layout.Dimensions {
				// 搜索按钮
				if app.searchButton.Clicked(gtx) && !app.isSearching {
					app.performSearch()
				}

				buttonText := "搜索"
				if app.isSearching {
					buttonText = "搜索中..."
				}

				btn := uniformButton(app.th, app.searchButton, buttonText, buttonColor)
				if app.isSearching {
					btn = uniformButton(app.th, app.searchButton, buttonText, color.NRGBA{R: 108, G: 117, B: 125, A: 255}) // 灰色
				}
				return btn(gtx)
			}),
			layout.Rigid(func(gtx layout.Context) layout.Dimensions {
				// 停止按钮 - 只在搜索时显示
				if !app.isSearching {
					return layout.Dimensions{}
				}

				return layout.Inset{Left: unit.Dp(12)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
					if app.stopButton.Clicked(gtx) {
						app.stopSearch()
					}

					btn := uniformButton(app.th, app.stopButton, "停止搜索", color.NRGBA{R: 220, G: 53, B: 69, A: 255}) // 红色
					return btn(gtx)
				})
			}),
		)
	})
}

// 渲染错误和成功信息
func (app *AppState) renderMessages(gtx layout.Context) layout.Dimensions {
	return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
		// 错误信息
		layout.Rigid(func(gtx layout.Context) layout.Dimensions {
			if app.errorMsg == "" {
				return layout.Dimensions{}
			}
			return layout.Inset{Top: 8, Left: 16, Right: 16}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
				label := material.Body2(app.th, app.errorMsg)
				label.Color = color.NRGBA{R: 220, G: 53, B: 69, A: 255} // 红色
				return label.Layout(gtx)
			})
		}),
		// 成功信息
		layout.Rigid(func(gtx layout.Context) layout.Dimensions {
			if app.successMsg == "" {
				return layout.Dimensions{}
			}
			return layout.Inset{Top: 8, Left: 16, Right: 16}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
				label := material.Body2(app.th, app.successMsg)
				label.Color = successColor // 绿色
				return label.Layout(gtx)
			})
		}),
	)
}

// ---------- 修改点 1: uniformButton（将按钮宽度固定） ----------
// 按钮统一包装：返回 layout.Widget，内部固定宽度（90dp）。
// 通过在布局阶段设置约束来保证所有结果按钮宽度一致。
// 仅影响结果项的按钮，其他界面按钮保持原样（未修改）。
func uniformButton(th *material.Theme, click *widget.Clickable, text string, bg color.NRGBA) layout.Widget {
	return func(gtx layout.Context) layout.Dimensions {
		btn := material.Button(th, click, text)
		btn.Background = bg
		btn.TextSize = unit.Sp(12)
		btn.Inset = layout.UniformInset(unit.Dp(6))

		// 固定按钮宽度为 90dp（转换为像素）
		w := gtx.Dp(unit.Dp(90))
		h := gtx.Dp(unit.Dp(36)) // 固定按钮高度为 36dp
		// 应用到当前约束
		gtx.Constraints.Min.X = w
		gtx.Constraints.Max.X = w
		gtx.Constraints.Min.Y = h
		gtx.Constraints.Max.Y = h

		return btn.Layout(gtx)
	}
}

// 渲染单个搜索结果项（保留原始显示并使用 uniformButton）
func (app *AppState) renderResultItem(gtx layout.Context, result *SearchResult) layout.Dimensions {
	return layout.Inset{Top: 4, Bottom: 4, Left: 4, Right: 4}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		// 绘制带背景的卡片
		background := color.NRGBA{R: 248, G: 249, B: 250, A: 255}

		defer clip.Rect{Max: image.Point{X: gtx.Constraints.Max.X, Y: gtx.Constraints.Max.Y}}.Push(gtx.Ops).Pop()
		paint.ColorOp{Color: background}.Add(gtx.Ops)
		paint.PaintOp{}.Add(gtx.Ops)

		return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
			// 文件路径和类型标识
			layout.Rigid(func(gtx layout.Context) layout.Dimensions {
				return layout.Inset{Top: 8, Bottom: 4, Left: 8, Right: 8}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
					return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
						layout.Rigid(func(gtx layout.Context) layout.Dimensions {
							// 类型标识
							typeText := "[文件]"
							typeColor := color.NRGBA{R: 0, G: 123, B: 255, A: 255} // 蓝色
							if result.IsDirectory {
								typeText = "[文件夹]"
								typeColor = color.NRGBA{R: 40, G: 167, B: 69, A: 255} // 绿色
							}

							label := material.Body2(app.th, typeText)
							label.Color = typeColor
							return layout.Inset{Right: unit.Dp(8)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
								return label.Layout(gtx)
							})
						}),
						layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
							body := material.Body2(app.th, result.Path)
							body.Color = textColor
							return body.Layout(gtx)
						}),
					)
				})
			}),
			// 按钮行 - 根据是否为目录显示不同按钮
			layout.Rigid(func(gtx layout.Context) layout.Dimensions {
				return layout.Inset{Top: 4, Bottom: 8, Left: 8, Right: 8}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
					if result.IsDirectory {
						// 文件夹按钮
						return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
							layout.Rigid(func(gtx layout.Context) layout.Dimensions {
								if result.OpenFileButton.Clicked(gtx) {
									go openFileDirectly(result.Path) // 打开文件夹
								}
								// 使用统一按钮（uniformButton 返回 layout.Widget）
								return uniformButton(app.th, result.OpenFileButton, "打开文件夹", successColor)(gtx)
							}),
							layout.Rigid(func(gtx layout.Context) layout.Dimensions {
								return layout.Inset{Left: unit.Dp(8)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
									if result.CopyButton.Clicked(gtx) {
										go func() {
											if err := copyToClipboard(result.Path); err == nil {
												app.successMsg = "路径已复制到剪贴板"
												// 3秒后清除成功消息
												go func() {
													time.Sleep(3 * time.Second)
													app.successMsg = ""
												}()
											} else {
												app.errorMsg = "复制失败: " + err.Error()
											}
										}()
									}
									return uniformButton(app.th, result.CopyButton, "复制路径", linkColor)(gtx)
								})
							}),
						)
					} else {
						// 文件按钮（保持原来的两行布局）
						return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
							// 第一行按钮
							layout.Rigid(func(gtx layout.Context) layout.Dimensions {
								return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
									layout.Rigid(func(gtx layout.Context) layout.Dimensions {
										if result.OpenFileButton.Clicked(gtx) {
											go openFileDirectly(result.Path)
										}
										return uniformButton(app.th, result.OpenFileButton, "打开文件", successColor)(gtx)
									}),
									layout.Rigid(func(gtx layout.Context) layout.Dimensions {
										return layout.Inset{Left: unit.Dp(8)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
											if result.NotepadButton.Clicked(gtx) {
												go openFileWithNotepad(result.Path)
											}
											return uniformButton(app.th, result.NotepadButton, "记事本", color.NRGBA{R: 108, G: 117, B: 125, A: 255})(gtx)
										})
									}),
									layout.Rigid(func(gtx layout.Context) layout.Dimensions {
										return layout.Inset{Left: unit.Dp(8)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
											if result.OpenWithButton.Clicked(gtx) {
												go openFileWithDialog(result.Path)
											}
											return uniformButton(app.th, result.OpenWithButton, "打开方式", color.NRGBA{R: 255, G: 193, B: 7, A: 255})(gtx)
										})
									}),
								)
							}),
							// 第二行按钮
							layout.Rigid(func(gtx layout.Context) layout.Dimensions {
								return layout.Inset{Top: unit.Dp(4)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
									return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
										layout.Rigid(func(gtx layout.Context) layout.Dimensions {
											if result.CopyButton.Clicked(gtx) {
												go func() {
													if err := copyToClipboard(result.Path); err == nil {
														app.successMsg = "路径已复制到剪贴板"
														// 3秒后清除成功消息
														go func() {
															time.Sleep(3 * time.Second)
															app.successMsg = ""
														}()
													} else {
														app.errorMsg = "复制失败: " + err.Error()
													}
												}()
											}
											return uniformButton(app.th, result.CopyButton, "复制路径", linkColor)(gtx)
										}),
										layout.Rigid(func(gtx layout.Context) layout.Dimensions {
											return layout.Inset{Left: unit.Dp(8)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
												if result.FolderButton.Clicked(gtx) {
													go openFolderInExplorer(result.Path)
												}
												return uniformButton(app.th, result.FolderButton, "打开文件夹", successColor)(gtx)
											})
										}),
									)
								})
							}),
						)
					}
				})
			}),
		)
	})
}

// 渲染搜索结果
func (app *AppState) renderResults(gtx layout.Context) layout.Dimensions {
	if len(app.results) == 0 {
		return layout.Dimensions{}
	}

	return layout.Inset{Top: 16, Left: 16, Right: 16, Bottom: 16}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
			layout.Rigid(func(gtx layout.Context) layout.Dimensions {
				title := material.Body1(app.th, fmt.Sprintf("找到 %d 个文件:", len(app.results)))
				title.Color = successColor
				return layout.Inset{Bottom: 8}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
					return title.Layout(gtx)
				})
			}),
			// 使用 material.List + widget.List 支持鼠标滚轮和滚动条
			layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
				return material.List(app.th, &app.resultList).Layout(gtx, len(app.results), func(gtx layout.Context, i int) layout.Dimensions {
					return app.renderResultItem(gtx, app.results[i])
				})
			}),
		)
	})
}

// 主UI渲染
func (app *AppState) render(gtx layout.Context) layout.Dimensions {
	// 处理键盘事件
	app.handleKeyEvents(gtx)

	// 强制UI刷新以显示停止按钮（保留原有行为）
	gtx.Execute(op.InvalidateCmd{})

	return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
		layout.Rigid(app.renderTitle),
		layout.Rigid(app.renderPathInput),
		layout.Rigid(app.renderFileNameInput),
		layout.Rigid(app.renderSearchButton),
		layout.Rigid(app.renderMessages), // 修改函数名
		layout.Flexed(1, app.renderResults),
	)
}

// 主事件循环
func (window *AppState) run(w *app.Window) {
	var ops op.Ops

	for !window.shouldExit {
		switch e := w.Event().(type) {
		case app.DestroyEvent:
			window.shouldExit = true
			return
		case app.FrameEvent:
			gtx := app.NewContext(&ops, e)
			window.render(gtx)
			e.Frame(gtx.Ops)
		}
	}
}

func main() {
	// 使用 channel 来优雅地处理退出信号
	done := make(chan bool, 1)

	go func() {
		defer func() {
			done <- true
		}()

		w := &app.Window{}
		w.Option(app.Title("文件搜索器"))
		w.Option(app.Size(800, 600))

		appState := newAppState()
		appState.run(w)
	}()

	// 启动应用主循环
	go func() {
		app.Main()
		done <- true
	}()

	// 等待应用退出
	<-done
	fmt.Println("应用已安全退出")
}
